package com.yt.common.utils;

import org.apache.commons.codec.binary.Base64;
import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;

import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.nio.charset.StandardCharsets;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @Description ：
 * @Author ： 解风
 * @Date ： 2018/01/02 15:40
 */
public class AESUtil {
    /*
     * 加密用的Key 可以用26个字母和数字组成 此处使用AES-128-CBC加密模式，key需要为16位。
     */
    private static String sKey = "3f9e319fce5c371d";
    private static String ivParameter = "3f9e319fce5c371d";
    private static final String KEY_ALGORITHM = "AES";
    private static final String DEFAULT_CIPHER_ALGORITHM = "AES/ECB/PKCS5Padding";


    //使其支持256位秘钥
    static {
        Field field = null;
        try {
            field = Class.forName("javax.crypto.JceSecurity").getDeclaredField("isRestricted");
            field.setAccessible(true);
            Field modifiersField = Field.class.getDeclaredField("modifiers");
            modifiersField.setAccessible(true);
            modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL);
            field.set(null, java.lang.Boolean.FALSE);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static String Encrypt(String encData ,String secretKey,String vector) throws Exception {

        if(secretKey == null) {
            return null;
        }
        if(secretKey.length() != 16) {
            return null;
        }
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        byte[] raw = secretKey.getBytes();
        SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
        IvParameterSpec iv = new IvParameterSpec(vector.getBytes());// 使用CBC模式，需要一个向量iv，可增加加密算法的强度
        cipher.init(Cipher.ENCRYPT_MODE, skeySpec, iv);
        byte[] encrypted = cipher.doFinal(encData.getBytes(StandardCharsets.UTF_8));
        return new BASE64Encoder().encode(encrypted);// 此处使用BASE64做转码。
    }


    // 加密
    public static String encrypt(String sSrc) throws Exception {
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        byte[] raw = sKey.getBytes();
        SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
        IvParameterSpec iv = new IvParameterSpec(ivParameter.getBytes());// 使用CBC模式，需要一个向量iv，可增加加密算法的强度
        cipher.init(Cipher.ENCRYPT_MODE, skeySpec, iv);
        byte[] encrypted = cipher.doFinal(sSrc.getBytes(StandardCharsets.UTF_8));
        String encoderMes = new BASE64Encoder().encode(encrypted);
        Pattern p = Pattern.compile("\\s*|\t|\r|\n");
        Matcher targetEncoderMes = p.matcher(encoderMes);
        return targetEncoderMes.replaceAll("");// 此处使用BASE64做转码。
    }

    // 加密
    public static String encryptByKey(String sSrc, String sKey) throws Exception {
        if (sKey == null) {
            return null;
        }
//        // 判断Key是否为16位
//        if (sKey.length() != 12) {
//            System.out.print("Key长度不是12位");
//            return null;
//        }
        byte[] raw = sKey.getBytes("utf-8");
        SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
        Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");//"算法/模式/补码方式"
        cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
        byte[] encrypted = cipher.doFinal(sSrc.getBytes("utf-8"));

        return new Base64().encodeToString(encrypted);//此处使用BASE64做转码功能，同时能起到2次加密的作用。
    }

    /*** AES 加密 * * @param message 待加密明文 * @param key 加解密 key * @return */
    public static String encrypt(String message, String key) {
        try {
            SecretKeySpec skeySpec = new SecretKeySpec(key.getBytes(), KEY_ALGORITHM);
            // Instantiate the cipher
            Cipher cipher = Cipher.getInstance(DEFAULT_CIPHER_ALGORITHM);
            cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
            byte[] encrypted = cipher.doFinal(message.getBytes());
            return Base64.encodeBase64String(encrypted);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    /*** AES 解密 * * @param encrypted 待解密密文 * @param key 加解密 key * @return */
    public static String decrypt(String encrypted, String key) {
        try {
            SecretKeySpec skeySpec = new SecretKeySpec(key.getBytes(), KEY_ALGORITHM);
            Cipher cipher = Cipher.getInstance(DEFAULT_CIPHER_ALGORITHM);
            cipher.init(Cipher.DECRYPT_MODE, skeySpec);
            byte[] original = cipher.doFinal(Base64.decodeBase64(encrypted));
            return new String(original);
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException("token解密失败！");
        }
    }

    // 解密
    public static String decrypt(String sSrc) throws Exception {
        try {
            byte[] raw = sKey.getBytes(StandardCharsets.US_ASCII);
            SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
            IvParameterSpec iv = new IvParameterSpec(ivParameter.getBytes());
            cipher.init(Cipher.DECRYPT_MODE, skeySpec, iv);
            byte[] encrypted1 = new BASE64Decoder().decodeBuffer(sSrc);// 先用base64解密
            byte[] original = cipher.doFinal(encrypted1);
            String originalString = new String(original, StandardCharsets.UTF_8);
            return originalString;
        } catch (Exception ex) {
            return null;
        }
    }

    public String decrypt(String sSrc,String key,String ivs) throws Exception {
        try {
            byte[] raw = key.getBytes(StandardCharsets.US_ASCII);
            SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
            IvParameterSpec iv = new IvParameterSpec(ivs.getBytes());
            cipher.init(Cipher.DECRYPT_MODE, skeySpec, iv);
            byte[] encrypted1 = new BASE64Decoder().decodeBuffer(sSrc);// 先用base64解密
            byte[] original = cipher.doFinal(encrypted1);
            String originalString = new String(original, StandardCharsets.UTF_8);
            return originalString;
        } catch (Exception ex) {
            return null;
        }
    }

    public static String encodeBytes(byte[] bytes) {
        StringBuffer strBuf = new StringBuffer();

        for (int i = 0; i < bytes.length; i++) {
            strBuf.append((char) (((bytes[i] >> 4) & 0xF) + ((int) 'a')));
            strBuf.append((char) (((bytes[i]) & 0xF) + ((int) 'a')));
        }

        return strBuf.toString();
    }

    public static void main(String[] args) {

        try {
            String s = encrypt("eosousoc49ba59abbe56e057546FCE3E7D5B4B7767E5EA6DA1044572");
            System.out.println("-----s-------"+s);
            String s1 = decrypt("bn6kO0QWCbXJ3B6qmvQ/DmC+3n/une25CK4paP3UsB6X9xkGIrAJ3Whc39pN1mqSwObOeyog6Zr4\n" +
                    "9IPklX0skUZdhjax/rE6HWNoKFtEk9Y2smyZTbOQEZN4k1mimoi5OoCOhnZ0qjDi8EcWJ0azdtfS\n" +
                    "+fd+zgY1aLH8oSyaFc+bmyqWqG2EFamgbZDPcHAyGxDUpf/hMLH0u1cu0ezEv71zh02mtVDoWuns\n" +
                    "sUqy2w6LcFiZxfz5wMxlCx56KnSp3wKQeqxj8v7BdsVKqKpU5IjE3cdQBE5/i4OAB0UXGQdsryKm\n" +
                    "N0dJDsRsLLux9eKJejW2W/XOawdQ+rsgPhPflC4AwXZFp2znsmvZqnwyRNJQXngs046Xi1UDJyto\n" +
                    "9yXGJg65QUu7eNMudq+Jb4s2IOsy2z4wCcurEg93fhZA90aa4v4gjhccbXW4hrFtv+U/9c7egQx9\n" +
                    "OuxWEyDg8S6kC94EAhyBT4d8eHk5pw13wq3RRGCnn3Vv5vhRee9umtqvmI6ZXvDFHBYQURyCgILp\n" +
                    "mZMb0jnFUKyBLoi7WEEAMq2hpfp7RuPof3p92c8tV9CoQd8ohhgR6s+mEgEdMsC3vWHMMgRK+YiZ\n" +
                    "SFXTf91yI5RCDS99s9OCIXEhW7mdsR6TQpdPMIwr8awS29dOmOvOTdBOePocBuhg+GNDZ1JsoTRi\n" +
                    "eUg5gQkfW1lp+CwM3lzfvb9x/ZBu/wVz8Vz3anfFaWe9SgGT1WHPFhCWPz2QIo0vf+eRruXc6Jk7\n" +
                    "C6bmaeVjQihtwp5ujoqzzLZchv9+pweho8Y3/Fja2t31bO2JZhip1y/Mq/BJpEjc1hJB6aLhPjni\n" +
                    "4PuW7HIOo/7p8FGORIhlwjRgdBtrsAAMybhjC9RCssKW+MQXkiNrN3vbho0D");
            System.out.println("-----s1-------"+s1);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}